var SocketServer = require('ws').Server;

class WebSocketServer {

    static isFunction(func) {
        return typeof func === 'function';
    }
    static getUUID() {
        return Math.random().toString(16).slice(-10);
    }
    
    extendConnection(connection) {
        connection.handler = {
            error(data) {
                connection.$emit('message[error]', {
                    message: '您的信息处理失败',
                    errorData: data
                });
            }
        };
        /**
         * socket触发客户端事件的函数
         * @param {*string} type 事件类型
         * @param {*object} data 发送对象
         * @param {*string} user 指定发送该事件的链接，默认为当前链接
         */
        connection.$emit = (type, data, user) => {
            data.type = type;
            let conn = connection;
            if (user) {
                conn = this.getConnectionByUser(user);
            }
            
            conn.send(JSON.stringify(data));
        };
        /**
         * 添加事件监听的函数
         * @param {*string} type 监听的事件类型
         * @param {*function} handleFunc 处理事件的函数
         */
        connection.$on = (type, handleFunc) => {

            if (connection.handler[type]) {
                return new Error('改事件监听已经添加');
            }

            connection.handler[type] = handleFunc;
        };

        return connection;
    }

    getConnectionByUser(user) {
        const caches = this.connCaches;
        const connectionArr = object.values(caches);
        let conn = null;
        connectionArr.some(connectionObj => {
            if (user === connectionObj.user) {
                conn = connectionObj.connection;
                return true;
            }
        });
        return conn;
    }

    getIdByConnection(connection) {
        const caches = this.connCaches;
        let id = '';
        for (let _id in caches) {
            let conn = caches[_id].connection;
            if (connection === conn) {
                id = _id;
                break;
            }
        }
        return id;
    }
    
    defineUtil(configFunc) {
        const utils = this.utils;
        if (!utils) {
            return new Error('程序出错了');
        }

        Object.assign(utils, configFunc(this.connCaches));
    }

    pushHistoryMessage(data) {
        const stack = this.historyStack;
        if(stack.length >= 60) {
            stack = stack.slice(-59);
        }
        stack.push(data);
        this.historyStack = stack;
    } 

    constructor(config) {
        const ws = this.ws = new SocketServer(config);
        const connCaches = this.connCaches = {};
        const historyMessage = this.historyStack = [];
        const self = this;

        const utils = this.utils = {
            checkLogin(user) {
                const connectionArr = Object.values(connCaches);
                return connectionArr.some(connectionObj => {
                    return connectionObj.user === user;
                });
            },
            login(conn, user, callback) {
                const connectionArr = Object.values(connCaches);
                connectionArr.some(connectionObj => {
                    if (connectionObj.connection === conn) {
                        connectionObj.user = user;
                        return true;
                    }
                });
                WebSocketServer.isFunction(callback) && callback();
            },
            getHistoryMessage() {
                return historyMessage;
            }
        };

        this.ws.on('connection', (connection) => {
            console.log('链接....')
            const _id = WebSocketServer.getUUID();
            this.connCaches[_id] = {
                connection,
                user: ''
            };
            const socket = this.extendConnection(connection);

            if (WebSocketServer.isFunction(this.connectionCallback)) {
                this.connectionCallback(socket, utils);
            }

            connection.on('message', (data) => {

                const dataJson = JSON.parse(data);
                const type = dataJson.type;
                delete dataJson.type;
                dataJson._id = this.getIdByConnection(connection);
                const callback = connection.handler[type];

                if (callback) {
                    type === 'message' && this.pushHistoryMessage(dataJson);
                    callback(dataJson);
                } else {
                    connection.handler.error(data);
                }
            });

            connection.on('error', () => {
                connection.close();
            })

            connection.on('close', () => {

                const id = this.getIdByConnection(connection);
                const connectionObj= this.connCaches[id];
                connectionObj.id = id;

                this.closeConnection(connection);
                
                if (WebSocketServer.isFunction(this.disconnectionCallback)) {
                    this.disconnectionCallback(connectionObj);
                }
            });
        });
    }

    closeConnection(conn) {
        const {connCaches} = this;
        let id = '';
        for (let _id in connCaches) {
            let oConn = connCaches[_id].connection;
            if (oConn === conn) {
                id = _id;
                break;
            }
        }

        delete connCaches[id];
    }

    onconnection(callback) {
        if (WebSocketServer.isFunction(callback)) {
            this.connectionCallback = callback;
        }
    }
    ondisconection(callback) {
        if (WebSocketServer.isFunction(callback)) {
            this.disconnectionCallback = callback;
        }
    }
    $broadcast(type, data) {
        const caches = this.connCaches;
        Object.values(caches).map(connectionObj => {
            const connection = connectionObj.connection;
            connection.$emit(type, data);
        });
    }

}

module.exports = WebSocketServer;
